package com.javastar.modules.security.filter;

import com.javastar.common.enums.TokenEnum;
import com.javastar.common.exception.AssertUtil;
import com.javastar.common.exception.ErrorCode;
import com.javastar.common.redis.RedisKey;
import com.javastar.common.redis.RedisUtil;
import com.javastar.common.utils.ConvertUtils;
import com.javastar.common.utils.JwtUtil;
import com.javastar.common.utils.ValidatorUtils;
import com.javastar.modules.admin.entity.SysUserEntity;
import com.javastar.modules.security.bean.SysUserDetails;
import com.javastar.modules.security.config.SysSecurityConfig;
import com.javastar.modules.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * token认证过滤器
 *
 * @author zxx
 * @date 2023/7/25
 */
@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 校验token，查询用户角色权限信息，封装设置到安全上下文中
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String adminToken = request.getHeader(TokenEnum.ADMIN_TOKEN.getTokenKey());
        Long userId;
        if (ValidatorUtils.isEmpty(adminToken) || !JwtUtil.validateToken(adminToken, TokenEnum.ADMIN_TOKEN) || (userId = JwtUtil.getId(adminToken, TokenEnum.ADMIN_TOKEN)) == null) {
            AssertUtil.error(ErrorCode.TOKEN_INVALID);
            return;
        }
        // 查询并封装用户信息
        SysUserEntity sysUserEntity = redisUtil.getBean(RedisKey.ADMIN_INFO, String.valueOf(userId), SysUserEntity.class);
        SysUserDetails sysUserDetails;
        if (ValidatorUtils.isEmpty(sysUserEntity)) {
            // db查询
            sysUserDetails = userDetailsService.getUsernamePasswordAuthenticationTokenById(userId);
        } else {
            List<String> permissions = redisUtil.getList(RedisKey.ADMIN_PERMISSIONS, String.valueOf(userId), String.class);
            sysUserDetails = ConvertUtils.sourceToTarget(sysUserEntity, SysUserDetails.class);
            sysUserDetails.setAuthorities(permissions.stream().map(permission -> new SimpleGrantedAuthority(permission)).toList());
        }
        // 将UserDetails封装为UsernamePasswordAuthenticationToken
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(sysUserDetails, null, sysUserDetails.getAuthorities());
        usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        filterChain.doFilter(request, response);
    }

    /**
     * 放行特定请求
     * @param request current HTTP request
     * @return
     * @throws ServletException
     */
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        String uri = request.getRequestURI();
        if (SysSecurityConfig.whitePath.contains(uri) || uri.startsWith("/webjars") || uri.startsWith("/v2") || uri.startsWith("/swagger-resources") || uri.startsWith("/swagger-ui") || uri.startsWith("/assets")) {
            return true;
        }

        return super.shouldNotFilter(request);
    }
}
